#include "wx/wxprec.h"
#include "wx/dcclient.h"
#include "wx/fontutil.h"
#include "wx/scrolwin.h"
#include "wx/gtk/win_gtk.h"
#include "wx/gtk/private.h"
#include <gdk/gdkx.h>

#define USE_PAINT_REGION 1

#include "bdiag.xbm"
#include "fdiag.xbm"
#include "cdiag.xbm"
#include "horiz.xbm"
#include "verti.xbm"
#include "cross.xbm"
#define  num_hatches 6

#define IS_15_PIX_HATCH(s) ((s)==wxCROSS_HATCH || (s)==wxHORIZONTAL_HATCH || (s)==wxVERTICAL_HATCH)
#define IS_16_PIX_HATCH(s) ((s)==wxBDIAGONAL_HATCH || (s)==wxCROSSDIAG_HATCH || (s)==wxFDIAGONAL_HATCH)


static GdkPixmap  *hatches[num_hatches];
static GdkPixmap **hatch_bitmap = ( GdkPixmap ** ) NULL;
extern GtkWidget *wxGetRootWindow();
const double RAD2DEG  = 180.0 / M_PI;
static inline double dmax( double a, double b ) { return a > b ? a : b; }
static inline double dmin( double a, double b ) { return a < b ? a : b; }
static inline double DegToRad( double deg ) { return ( deg * M_PI ) / 180.0; }

#include "gdk/gdkprivate.h"

static
void gdk_wx_draw_bitmap( GdkDrawable *drawable, GdkGC *gc, GdkDrawable *src, gint xsrc, gint ysrc,
                         gint xdest, gint ydest, gint width, gint height ) {
  wxCHECK_RET( drawable, _T( "NULL drawable in gdk_wx_draw_bitmap" ) );
  wxCHECK_RET( src, _T( "NULL src in gdk_wx_draw_bitmap" ) );
  wxCHECK_RET( gc, _T( "NULL gc in gdk_wx_draw_bitmap" ) );
  gint src_width, src_height;
  gdk_drawable_get_size( src, &src_width, &src_height );
  if( width == -1 ) {
    width = src_width;
  }
  if( height == -1 ) {
    height = src_height;
  }
  XCopyPlane( GDK_WINDOW_XDISPLAY( drawable ), GDK_WINDOW_XID( src ), GDK_WINDOW_XID( drawable ), GDK_GC_XGC( gc ),
              xsrc, ysrc, width, height, 0, 0, 1 );
}

enum wxPoolGCType {
  wxGC_ERROR = 0,
  wxTEXT_MONO,
  wxBG_MONO,
  wxPEN_MONO,
  wxBRUSH_MONO,
  wxTEXT_COLOUR,
  wxBG_COLOUR,
  wxPEN_COLOUR,
  wxBRUSH_COLOUR,
  wxTEXT_SCREEN,
  wxBG_SCREEN,
  wxPEN_SCREEN,
  wxBRUSH_SCREEN
};

struct wxGC {
  GdkGC        *m_gc;
  wxPoolGCType  m_type;
  bool          m_used;
};

#define GC_POOL_ALLOC_SIZE 100

static int wxGCPoolSize = 0;

static wxGC *wxGCPool = NULL;

static void wxInitGCPool() {
  wxGCPoolSize = GC_POOL_ALLOC_SIZE;
  wxGCPool = ( wxGC * )malloc( wxGCPoolSize * sizeof( wxGC ) );
  if( wxGCPool == NULL ) {
    wxFAIL_MSG( wxT( "Cannot allocate GC pool" ) );
    return;
  }
  memset( wxGCPool, 0, wxGCPoolSize * sizeof( wxGC ) );
}

static void wxCleanUpGCPool() {
  for( int i = 0; i < wxGCPoolSize; i++ ) {
    if( wxGCPool[i].m_gc ) {
      g_object_unref( wxGCPool[i].m_gc );
    }
  }
  free( wxGCPool );
  wxGCPool = NULL;
  wxGCPoolSize = 0;
}

static GdkGC* wxGetPoolGC( GdkWindow *window, wxPoolGCType type ) {
  wxGC *pptr;
  for( int i = 0; i < wxGCPoolSize; i++ ) {
    if( !wxGCPool[i].m_gc ) {
      wxGCPool[i].m_gc = gdk_gc_new( window );
      gdk_gc_set_exposures( wxGCPool[i].m_gc, FALSE );
      wxGCPool[i].m_type = type;
      wxGCPool[i].m_used = false;
    }
    if( ( !wxGCPool[i].m_used ) && ( wxGCPool[i].m_type == type ) ) {
      wxGCPool[i].m_used = true;
      return wxGCPool[i].m_gc;
    }
  }
  pptr = ( wxGC * )realloc( wxGCPool, ( wxGCPoolSize + GC_POOL_ALLOC_SIZE ) * sizeof( wxGC ) );
  if( pptr != NULL ) {
    wxGCPool = pptr;
    memset( &wxGCPool[wxGCPoolSize], 0, GC_POOL_ALLOC_SIZE * sizeof( wxGC ) );
    wxGCPool[wxGCPoolSize].m_gc = gdk_gc_new( window );
    gdk_gc_set_exposures( wxGCPool[wxGCPoolSize].m_gc, FALSE );
    wxGCPool[wxGCPoolSize].m_type = type;
    wxGCPool[wxGCPoolSize].m_used = true;
    wxGCPoolSize += GC_POOL_ALLOC_SIZE;
    return wxGCPool[wxGCPoolSize - GC_POOL_ALLOC_SIZE].m_gc;
  }
  wxFAIL_MSG( wxT( "No GC available" ) );
  return ( GdkGC* ) NULL;
}

static void wxFreePoolGC( GdkGC *gc ) {
  for( int i = 0; i < wxGCPoolSize; i++ ) {
    if( wxGCPool[i].m_gc == gc ) {
      wxGCPool[i].m_used = false;
      return;
    }
  }
  wxFAIL_MSG( wxT( "Wrong GC" ) );
}

IMPLEMENT_DYNAMIC_CLASS( wxWindowDC, wxDC )

wxWindowDC::wxWindowDC() {
  m_penGC = ( GdkGC * ) NULL;
  m_brushGC = ( GdkGC * ) NULL;
  m_textGC = ( GdkGC * ) NULL;
  m_bgGC = ( GdkGC * ) NULL;
  m_cmap = ( GdkColormap * ) NULL;
  m_isMemDC = false;
  m_isScreenDC = false;
  m_owner = ( wxWindow * )NULL;
  m_context = ( PangoContext * )NULL;
  m_layout = ( PangoLayout * )NULL;
  m_fontdesc = ( PangoFontDescription * )NULL;
}

wxWindowDC::wxWindowDC( wxWindow *window ) {
  wxCHECK_RET( window, wxT( "DC needs a window" ) );
  m_penGC = ( GdkGC * ) NULL;
  m_brushGC = ( GdkGC * ) NULL;
  m_textGC = ( GdkGC * ) NULL;
  m_bgGC = ( GdkGC * ) NULL;
  m_cmap = ( GdkColormap * ) NULL;
  m_owner = ( wxWindow * )NULL;
  m_isMemDC = false;
  m_isScreenDC = false;
  m_font = window->GetFont();
  GtkWidget *widget = window->m_wxwindow;
  if( !widget ) {
    window = window->GetParent();
    widget = window->m_wxwindow;
  }
  wxASSERT_MSG( widget, wxT( "DC needs a widget" ) );
  m_context = window->GtkGetPangoDefaultContext();
  m_layout = pango_layout_new( m_context );
  m_fontdesc = pango_font_description_copy( widget->style->font_desc );
  GtkPizza *pizza = GTK_PIZZA( widget );
  m_window = pizza->bin_window;
  if( !m_window ) {
    m_ok = true;
    return;
  }
  m_cmap = gtk_widget_get_colormap( widget ? widget : window->m_widget );
  SetUpDC();
  m_owner = window;
  if( m_owner && m_owner->m_wxwindow && ( m_owner->GetLayoutDirection() == wxLayout_RightToLeft ) ) {
    m_signX = -1;
    m_deviceOriginX = m_owner->GetClientSize().x;
  }
}

wxWindowDC::~wxWindowDC() {
  Destroy();
  if( m_layout ) {
    g_object_unref( m_layout );
  }
  if( m_fontdesc ) {
    pango_font_description_free( m_fontdesc );
  }
}

void wxWindowDC::SetUpDC() {
  m_ok = true;
  wxASSERT_MSG( !m_penGC, wxT( "GCs already created" ) );
  if( m_isScreenDC ) {
    m_penGC = wxGetPoolGC( m_window, wxPEN_SCREEN );
    m_brushGC = wxGetPoolGC( m_window, wxBRUSH_SCREEN );
    m_textGC = wxGetPoolGC( m_window, wxTEXT_SCREEN );
    m_bgGC = wxGetPoolGC( m_window, wxBG_SCREEN );
  } else if( m_isMemDC && ( ( ( wxMemoryDC* )this )->m_selected.GetDepth() == 1 ) ) {
    m_penGC = wxGetPoolGC( m_window, wxPEN_MONO );
    m_brushGC = wxGetPoolGC( m_window, wxBRUSH_MONO );
    m_textGC = wxGetPoolGC( m_window, wxTEXT_MONO );
    m_bgGC = wxGetPoolGC( m_window, wxBG_MONO );
  } else {
    m_penGC = wxGetPoolGC( m_window, wxPEN_COLOUR );
    m_brushGC = wxGetPoolGC( m_window, wxBRUSH_COLOUR );
    m_textGC = wxGetPoolGC( m_window, wxTEXT_COLOUR );
    m_bgGC = wxGetPoolGC( m_window, wxBG_COLOUR );
  }
  m_backgroundBrush = *wxWHITE_BRUSH;
  m_backgroundBrush.GetColour().CalcPixel( m_cmap );
  const GdkColor *bg_col = m_backgroundBrush.GetColour().GetColor();
  m_textForegroundColour.CalcPixel( m_cmap );
  gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
  m_textBackgroundColour.CalcPixel( m_cmap );
  gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() );
  gdk_gc_set_fill( m_textGC, GDK_SOLID );
  gdk_gc_set_colormap( m_textGC, m_cmap );
  m_pen.GetColour().CalcPixel( m_cmap );
  gdk_gc_set_foreground( m_penGC, m_pen.GetColour().GetColor() );
  gdk_gc_set_background( m_penGC, bg_col );
  gdk_gc_set_line_attributes( m_penGC, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_ROUND );
  m_brush.GetColour().CalcPixel( m_cmap );
  gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() );
  gdk_gc_set_background( m_brushGC, bg_col );
  gdk_gc_set_fill( m_brushGC, GDK_SOLID );
  gdk_gc_set_background( m_bgGC, bg_col );
  gdk_gc_set_foreground( m_bgGC, bg_col );
  gdk_gc_set_fill( m_bgGC, GDK_SOLID );
  gdk_gc_set_function( m_textGC, GDK_COPY );
  gdk_gc_set_function( m_brushGC, GDK_COPY );
  gdk_gc_set_function( m_penGC, GDK_COPY );
  gdk_gc_set_clip_rectangle( m_penGC, ( GdkRectangle * ) NULL );
  gdk_gc_set_clip_rectangle( m_brushGC, ( GdkRectangle * ) NULL );
  gdk_gc_set_clip_rectangle( m_textGC, ( GdkRectangle * ) NULL );
  gdk_gc_set_clip_rectangle( m_bgGC, ( GdkRectangle * ) NULL );
  if( !hatch_bitmap ) {
    hatch_bitmap    = hatches;
    hatch_bitmap[0] = gdk_bitmap_create_from_data( ( GdkWindow * ) NULL, bdiag_bits, bdiag_width, bdiag_height );
    hatch_bitmap[1] = gdk_bitmap_create_from_data( ( GdkWindow * ) NULL, cdiag_bits, cdiag_width, cdiag_height );
    hatch_bitmap[2] = gdk_bitmap_create_from_data( ( GdkWindow * ) NULL, fdiag_bits, fdiag_width, fdiag_height );
    hatch_bitmap[3] = gdk_bitmap_create_from_data( ( GdkWindow * ) NULL, cross_bits, cross_width, cross_height );
    hatch_bitmap[4] = gdk_bitmap_create_from_data( ( GdkWindow * ) NULL, horiz_bits, horiz_width, horiz_height );
    hatch_bitmap[5] = gdk_bitmap_create_from_data( ( GdkWindow * ) NULL, verti_bits, verti_width, verti_height );
  }
}

void wxWindowDC::DoGetSize( int* width, int* height ) const {
  wxCHECK_RET( m_owner, _T( "GetSize() doesn't work without window" ) );
  m_owner->GetSize( width, height );
}

extern bool wxDoFloodFill( wxDC *dc, wxCoord x, wxCoord y, const wxColour & col, int style );

bool wxWindowDC::DoFloodFill( wxCoord x, wxCoord y, const wxColour& col, int style ) {
  return wxDoFloodFill( this, x, y, col, style );
}

bool wxWindowDC::DoGetPixel( wxCoord x1, wxCoord y1, wxColour *col ) const {
  wxMemoryDC memdc;
  wxBitmap bitmap( 1, 1 );
  memdc.SelectObject( bitmap );
  memdc.Blit( 0, 0, 1, 1, ( wxDC* ) this, x1, y1 );
  memdc.SelectObject( wxNullBitmap );
  wxImage image = bitmap.ConvertToImage();
  col->Set( image.GetRed( 0, 0 ), image.GetGreen( 0, 0 ), image.GetBlue( 0, 0 ) );
  return true;
}

void wxWindowDC::DoDrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2 ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( m_pen.GetStyle() != wxTRANSPARENT ) {
    if( m_window ) {
      gdk_draw_line( m_window, m_penGC, XLOG2DEV( x1 ), YLOG2DEV( y1 ), XLOG2DEV( x2 ), YLOG2DEV( y2 ) );
    }
    CalcBoundingBox( x1, y1 );
    CalcBoundingBox( x2, y2 );
  }
}

void wxWindowDC::DoCrossHair( wxCoord x, wxCoord y ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( m_pen.GetStyle() != wxTRANSPARENT ) {
    int w = 0;
    int h = 0;
    GetSize( &w, &h );
    wxCoord xx = XLOG2DEV( x );
    wxCoord yy = YLOG2DEV( y );
    if( m_window ) {
      gdk_draw_line( m_window, m_penGC, 0, yy, XLOG2DEVREL( w ), yy );
      gdk_draw_line( m_window, m_penGC, xx, 0, xx, YLOG2DEVREL( h ) );
    }
  }
}

void wxWindowDC::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord xc, wxCoord yc ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  wxCoord xx1 = XLOG2DEV( x1 );
  wxCoord yy1 = YLOG2DEV( y1 );
  wxCoord xx2 = XLOG2DEV( x2 );
  wxCoord yy2 = YLOG2DEV( y2 );
  wxCoord xxc = XLOG2DEV( xc );
  wxCoord yyc = YLOG2DEV( yc );
  double dx = xx1 - xxc;
  double dy = yy1 - yyc;
  double radius = sqrt( ( double )( dx * dx + dy * dy ) );
  wxCoord   r      = ( wxCoord )radius;
  double radius1, radius2;
  if( xx1 == xx2 && yy1 == yy2 ) {
    radius1 = 0.0;
    radius2 = 360.0;
  } else if( wxIsNullDouble( radius ) ) {
    radius1 =
      radius2 = 0.0;
  } else {
    radius1 = ( xx1 - xxc == 0 ) ? ( yy1 - yyc < 0 ) ? 90.0 : -90.0 :
              -atan2( double( yy1 - yyc ), double( xx1 - xxc ) ) * RAD2DEG;
    radius2 = ( xx2 - xxc == 0 ) ? ( yy2 - yyc < 0 ) ? 90.0 : -90.0 :
              -atan2( double( yy2 - yyc ), double( xx2 - xxc ) ) * RAD2DEG;
  }
  wxCoord alpha1 = wxCoord( radius1 * 64.0 );
  wxCoord alpha2 = wxCoord( ( radius2 - radius1 ) * 64.0 );
  while( alpha2 <= 0 ) {
    alpha2 += 360 * 64;
  }
  while( alpha1 > 360 * 64 ) {
    alpha1 -= 360 * 64;
  }
  if( m_window ) {
    if( m_brush.GetStyle() != wxTRANSPARENT ) {
      if( ( m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE ) && ( m_brush.GetStipple()->GetMask() ) ) {
        gdk_gc_set_ts_origin( m_textGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_arc( m_window, m_textGC, TRUE, xxc - r, yyc - r, 2 * r, 2 * r, alpha1, alpha2 );
        gdk_gc_set_ts_origin( m_textGC, 0, 0 );
      } else if( IS_15_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xxc - r, yyc - r, 2 * r, 2 * r, alpha1, alpha2 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( IS_16_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xxc - r, yyc - r, 2 * r, 2 * r, alpha1, alpha2 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( m_brush.GetStyle() == wxSTIPPLE ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xxc - r, yyc - r, 2 * r, 2 * r, alpha1, alpha2 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else {
        gdk_draw_arc( m_window, m_brushGC, TRUE, xxc - r, yyc - r, 2 * r, 2 * r, alpha1, alpha2 );
      }
    }
    if( m_pen.GetStyle() != wxTRANSPARENT ) {
      gdk_draw_arc( m_window, m_penGC, FALSE, xxc - r, yyc - r, 2 * r, 2 * r, alpha1, alpha2 );
      if( ( m_brush.GetStyle() != wxTRANSPARENT ) && ( alpha2 - alpha1 != 360 * 64 ) ) {
        gdk_draw_line( m_window, m_penGC, xx1, yy1, xxc, yyc );
        gdk_draw_line( m_window, m_penGC, xxc, yyc, xx2, yy2 );
      }
    }
  }
  CalcBoundingBox( x1, y1 );
  CalcBoundingBox( x2, y2 );
}

void wxWindowDC::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double sa, double ea ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  wxCoord xx = XLOG2DEV( x );
  wxCoord yy = YLOG2DEV( y );
  wxCoord ww = m_signX * XLOG2DEVREL( width );
  wxCoord hh = m_signY * YLOG2DEVREL( height );
  if( ww < 0 ) {
    ww = -ww;
    xx = xx - ww;
  }
  if( hh < 0 ) {
    hh = -hh;
    yy = yy - hh;
  }
  if( m_window ) {
    wxCoord start = wxCoord( sa * 64.0 );
    wxCoord end = wxCoord( ( ea - sa ) * 64.0 );
    if( m_brush.GetStyle() != wxTRANSPARENT ) {
      if( ( m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE ) && ( m_brush.GetStipple()->GetMask() ) ) {
        gdk_gc_set_ts_origin( m_textGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy, ww, hh, start, end );
        gdk_gc_set_ts_origin( m_textGC, 0, 0 );
      } else if( IS_15_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( IS_16_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( m_brush.GetStyle() == wxSTIPPLE ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else {
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
      }
    }
    if( m_pen.GetStyle() != wxTRANSPARENT ) {
      gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, start, end );
    }
  }
  CalcBoundingBox( x, y );
  CalcBoundingBox( x + width, y + height );
}

void wxWindowDC::DoDrawPoint( wxCoord x, wxCoord y ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( ( m_pen.GetStyle() != wxTRANSPARENT ) && m_window ) {
    gdk_draw_point( m_window, m_penGC, XLOG2DEV( x ), YLOG2DEV( y ) );
  }
  CalcBoundingBox( x, y );
}

void wxWindowDC::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( m_pen.GetStyle() == wxTRANSPARENT ) {
    return;
  }
  if( n <= 0 ) {
    return;
  }
  const bool doScale = xoffset != 0 || yoffset != 0 || XLOG2DEV( 10 ) != 10 || YLOG2DEV( 10 ) != 10;
  GdkPoint* gpts = reinterpret_cast<GdkPoint*>( points );
  if( doScale ) {
    gpts = new GdkPoint[n];
  }
  for( int i = 0; i < n; i++ ) {
    if( doScale ) {
      gpts[i].x = XLOG2DEV( points[i].x + xoffset );
      gpts[i].y = YLOG2DEV( points[i].y + yoffset );
    }
    CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset );
  }
  if( m_window ) {
    gdk_draw_lines( m_window, m_penGC, gpts, n );
  }
  if( doScale ) {
    delete[] gpts;
  }
}

void wxWindowDC::DoDrawPolygon( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, int ( fillStyle ) ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( n <= 0 ) {
    return;
  }
  const bool doScale = xoffset != 0 || yoffset != 0 || XLOG2DEV( 10 ) != 10 || YLOG2DEV( 10 ) != 10;
  GdkPoint* gdkpoints = reinterpret_cast<GdkPoint*>( points );
  if( doScale ) {
    gdkpoints = new GdkPoint[n];
  }
  int i;
  for( i = 0 ; i < n ; i++ ) {
    if( doScale ) {
      gdkpoints[i].x = XLOG2DEV( points[i].x + xoffset );
      gdkpoints[i].y = YLOG2DEV( points[i].y + yoffset );
    }
    CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset );
  }
  if( m_window ) {
    if( m_brush.GetStyle() != wxTRANSPARENT ) {
      if( ( m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE ) && ( m_brush.GetStipple()->GetMask() ) ) {
        gdk_gc_set_ts_origin( m_textGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_polygon( m_window, m_textGC, TRUE, gdkpoints, n );
        gdk_gc_set_ts_origin( m_textGC, 0, 0 );
      } else if( IS_15_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
        gdk_draw_polygon( m_window, m_brushGC, TRUE, gdkpoints, n );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( IS_16_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
        gdk_draw_polygon( m_window, m_brushGC, TRUE, gdkpoints, n );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( m_brush.GetStyle() == wxSTIPPLE ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_polygon( m_window, m_brushGC, TRUE, gdkpoints, n );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else {
        gdk_draw_polygon( m_window, m_brushGC, TRUE, gdkpoints, n );
      }
    }
    if( m_pen.GetStyle() != wxTRANSPARENT ) {
      gdk_draw_polygon( m_window, m_penGC, FALSE, gdkpoints, n );
    }
  }
  if( doScale ) {
    delete[] gdkpoints;
  }
}

void wxWindowDC::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  wxCoord xx = XLOG2DEV( x );
  wxCoord yy = YLOG2DEV( y );
  wxCoord ww = m_signX * XLOG2DEVREL( width );
  wxCoord hh = m_signY * YLOG2DEVREL( height );
  if( ww == 0 || hh == 0 ) {
    return;
  }
  if( ww < 0 ) {
    ww = -ww;
    xx = xx - ww;
  }
  if( hh < 0 ) {
    hh = -hh;
    yy = yy - hh;
  }
  if( m_window ) {
    if( m_brush.GetStyle() != wxTRANSPARENT ) {
      if( ( m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE ) && ( m_brush.GetStipple()->GetMask() ) ) {
        gdk_gc_set_ts_origin( m_textGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_rectangle( m_window, m_textGC, TRUE, xx, yy, ww, hh );
        gdk_gc_set_ts_origin( m_textGC, 0, 0 );
      } else if( IS_15_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( IS_16_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( m_brush.GetStyle() == wxSTIPPLE ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else {
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
      }
    }
    if( m_pen.GetStyle() != wxTRANSPARENT ) {
      if( ( m_pen.GetWidth() == 2 ) && ( m_pen.GetCap() == wxCAP_ROUND ) &&
          ( m_pen.GetJoin() == wxJOIN_ROUND ) && ( m_pen.GetStyle() == wxSOLID ) ) {
        gdk_gc_set_line_attributes( m_penGC, 1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
        if( m_signX == -1 ) {
          gdk_draw_rectangle( m_window, m_penGC, FALSE, xx + 1, yy, ww - 2, hh - 2 );
          gdk_draw_rectangle( m_window, m_penGC, FALSE, xx, yy - 1, ww, hh );
        } else {
          gdk_draw_rectangle( m_window, m_penGC, FALSE, xx, yy, ww - 2, hh - 2 );
          gdk_draw_rectangle( m_window, m_penGC, FALSE, xx - 1, yy - 1, ww, hh );
        }
        gdk_gc_set_line_attributes( m_penGC, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
      } else {
        gdk_draw_rectangle( m_window, m_penGC, FALSE, xx, yy, ww - 1, hh - 1 );
      }
    }
  }
  CalcBoundingBox( x, y );
  CalcBoundingBox( x + width, y + height );
}

void wxWindowDC::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( radius < 0.0 ) {
    radius = - radius * ( ( width < height ) ? width : height );
  }
  wxCoord xx = XLOG2DEV( x );
  wxCoord yy = YLOG2DEV( y );
  wxCoord ww = m_signX * XLOG2DEVREL( width );
  wxCoord hh = m_signY * YLOG2DEVREL( height );
  wxCoord rr = XLOG2DEVREL( ( wxCoord )radius );
  if( ww < 0 ) {
    ww = -ww;
    xx = xx - ww;
  }
  if( hh < 0 ) {
    hh = -hh;
    yy = yy - hh;
  }
  if( rr == 0 ) {
    DrawRectangle( x, y, width, height );
    return;
  }
  if( ww == 0 || hh == 0 ) {
    return;
  }
  if( m_pen.GetStyle() != wxTRANSPARENT ) {
    ww--;
    hh--;
  }
  if( m_window ) {
    wxCoord dd = 2 * rr;
    if( dd > ww ) {
      dd = ww;
    }
    if( dd > hh ) {
      dd = hh;
    }
    rr = dd / 2;
    if( m_brush.GetStyle() != wxTRANSPARENT ) {
      if( ( m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE ) && ( m_brush.GetStipple()->GetMask() ) ) {
        gdk_gc_set_ts_origin( m_textGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_rectangle( m_window, m_textGC, TRUE, xx + rr, yy, ww - dd + 1, hh );
        gdk_draw_rectangle( m_window, m_textGC, TRUE, xx, yy + rr, ww, hh - dd + 1 );
        gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy, dd, dd, 90 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_textGC, TRUE, xx + ww - dd, yy, dd, dd, 0, 90 * 64 );
        gdk_draw_arc( m_window, m_textGC, TRUE, xx + ww - dd, yy + hh - dd, dd, dd, 270 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy + hh - dd, dd, dd, 180 * 64, 90 * 64 );
        gdk_gc_set_ts_origin( m_textGC, 0, 0 );
      } else if( IS_15_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx + rr, yy, ww - dd + 1, hh );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy + rr, ww, hh - dd + 1 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy, dd, dd, 0, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy + hh - dd, dd, dd, 270 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy + hh - dd, dd, dd, 180 * 64, 90 * 64 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( IS_16_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx + rr, yy, ww - dd + 1, hh );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy + rr, ww, hh - dd + 1 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy, dd, dd, 0, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy + hh - dd, dd, dd, 270 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy + hh - dd, dd, dd, 180 * 64, 90 * 64 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( m_brush.GetStyle() == wxSTIPPLE ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx + rr, yy, ww - dd + 1, hh );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy + rr, ww, hh - dd + 1 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy, dd, dd, 0, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy + hh - dd, dd, dd, 270 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy + hh - dd, dd, dd, 180 * 64, 90 * 64 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else {
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx + rr, yy, ww - dd + 1, hh );
        gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy + rr, ww, hh - dd + 1 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy, dd, dd, 0, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx + ww - dd, yy + hh - dd, dd, dd, 270 * 64, 90 * 64 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy + hh - dd, dd, dd, 180 * 64, 90 * 64 );
      }
    }
    if( m_pen.GetStyle() != wxTRANSPARENT ) {
      gdk_draw_line( m_window, m_penGC, xx + rr + 1, yy, xx + ww - rr, yy );
      gdk_draw_line( m_window, m_penGC, xx + rr + 1, yy + hh, xx + ww - rr, yy + hh );
      gdk_draw_line( m_window, m_penGC, xx, yy + rr + 1, xx, yy + hh - rr );
      gdk_draw_line( m_window, m_penGC, xx + ww, yy + rr + 1, xx + ww, yy + hh - rr );
      gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, dd, dd, 90 * 64, 90 * 64 );
      gdk_draw_arc( m_window, m_penGC, FALSE, xx + ww - dd, yy, dd, dd, 0, 90 * 64 );
      gdk_draw_arc( m_window, m_penGC, FALSE, xx + ww - dd, yy + hh - dd, dd, dd, 270 * 64, 90 * 64 );
      gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy + hh - dd, dd, dd, 180 * 64, 90 * 64 );
    }
  }
  CalcBoundingBox( x, y );
  CalcBoundingBox( x + width, y + height );
}

void wxWindowDC::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  wxCoord xx = XLOG2DEV( x );
  wxCoord yy = YLOG2DEV( y );
  wxCoord ww = m_signX * XLOG2DEVREL( width );
  wxCoord hh = m_signY * YLOG2DEVREL( height );
  if( ww < 0 ) {
    ww = -ww;
    xx = xx - ww;
  }
  if( hh < 0 ) {
    hh = -hh;
    yy = yy - hh;
  }
  if( m_window ) {
    if( m_brush.GetStyle() != wxTRANSPARENT ) {
      if( ( m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE ) && ( m_brush.GetStipple()->GetMask() ) ) {
        gdk_gc_set_ts_origin( m_textGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy, ww, hh, 0, 360 * 64 );
        gdk_gc_set_ts_origin( m_textGC, 0, 0 );
      } else if( IS_15_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360 * 64 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( IS_16_PIX_HATCH( m_brush.GetStyle() ) ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360 * 64 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else if( m_brush.GetStyle() == wxSTIPPLE ) {
        gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
                              m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360 * 64 );
        gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
      } else {
        gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360 * 64 );
      }
    }
    if( m_pen.GetStyle() != wxTRANSPARENT ) {
      gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, 0, 360 * 64 );
    }
  }
  CalcBoundingBox( x, y );
  CalcBoundingBox( x + width, y + height );
}

void wxWindowDC::DoDrawIcon( const wxIcon &icon, wxCoord x, wxCoord y ) {
  DoDrawBitmap( ( const wxBitmap& )icon, x, y, true );
}

void wxWindowDC::DoDrawBitmap( const wxBitmap &bitmap, wxCoord x, wxCoord y, bool useMask ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  wxCHECK_RET( bitmap.Ok(), wxT( "invalid bitmap" ) );
  bool is_mono = bitmap.GetDepth() == 1;
  int xx = XLOG2DEV( x );
  int yy = YLOG2DEV( y );
  int w = bitmap.GetWidth();
  int h = bitmap.GetHeight();
  if( m_owner && m_owner->GetLayoutDirection() == wxLayout_RightToLeft ) {
    xx -= w;
  }
  CalcBoundingBox( x, y );
  CalcBoundingBox( x + w, y + h );
  if( !m_window ) {
    return;
  }
  int ww = XLOG2DEVREL( w );
  int hh = YLOG2DEVREL( h );
  if( !m_currentClippingRegion.IsNull() ) {
    wxRegion tmp( xx, yy, ww, hh );
    tmp.Intersect( m_currentClippingRegion );
    if( tmp.IsEmpty() ) {
      return;
    }
  }
  wxBitmap use_bitmap = bitmap;
  if( ( w != ww ) || ( h != hh ) ) {
    use_bitmap = use_bitmap.Rescale( 0, 0, ww, hh, ww, hh );
  }
  #if !GTK_CHECK_VERSION(2,2,0)
  use_bitmap.GetPixmap();
  #endif
  GdkBitmap *mask = ( GdkBitmap * ) NULL;
  if( useMask && use_bitmap.GetMask() ) {
    mask = use_bitmap.GetMask()->GetBitmap();
  }
  GdkGC* use_gc = is_mono ? m_textGC : m_penGC;
  GdkBitmap *new_mask = ( GdkBitmap* ) NULL;
  if( mask != NULL ) {
    if( !m_currentClippingRegion.IsNull() ) {
      GdkColor col;
      new_mask = gdk_pixmap_new( wxGetRootWindow()->window, ww, hh, 1 );
      GdkGC *gc = gdk_gc_new( new_mask );
      col.pixel = 0;
      gdk_gc_set_foreground( gc, &col );
      gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, ww, hh );
      col.pixel = 0;
      gdk_gc_set_background( gc, &col );
      col.pixel = 1;
      gdk_gc_set_foreground( gc, &col );
      gdk_gc_set_clip_region( gc, m_currentClippingRegion.GetRegion() );
      gdk_gc_set_clip_origin( gc, -xx, -yy );
      gdk_gc_set_fill( gc, GDK_OPAQUE_STIPPLED );
      gdk_gc_set_stipple( gc, mask );
      gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, ww, hh );
      mask = new_mask;
      g_object_unref( gc );
    }
    gdk_gc_set_clip_mask( use_gc, mask );
    gdk_gc_set_clip_origin( use_gc, xx, yy );
  }
  if( is_mono ) {
    GdkPixmap *bitmap2 = gdk_pixmap_new( wxGetRootWindow()->window, ww, hh, -1 );
    GdkGC *gc = gdk_gc_new( bitmap2 );
    gdk_gc_set_foreground( gc, m_textForegroundColour.GetColor() );
    gdk_gc_set_background( gc, m_textBackgroundColour.GetColor() );
    gdk_wx_draw_bitmap( bitmap2, gc, use_bitmap.GetPixmap(), 0, 0, 0, 0, -1, -1 );
    gdk_draw_drawable( m_window, use_gc, bitmap2, 0, 0, xx, yy, -1, -1 );
    g_object_unref( bitmap2 );
    g_object_unref( gc );
  } else {
    #if GTK_CHECK_VERSION(2,2,0)
    if( !gtk_check_version( 2, 2, 0 ) && use_bitmap.HasPixbuf() ) {
      gdk_draw_pixbuf( m_window, use_gc,
                       use_bitmap.GetPixbuf(),
                       0, 0, xx, yy, -1, -1,
                       GDK_RGB_DITHER_NORMAL, xx, yy );
    } else
    #endif
    {
      gdk_draw_drawable( m_window, use_gc, use_bitmap.GetPixmap(), 0, 0, xx, yy, -1, -1 );
    }
  }
  if( mask != NULL ) {
    gdk_gc_set_clip_mask( use_gc, NULL );
    gdk_gc_set_clip_origin( use_gc, 0, 0 );
    if( !m_currentClippingRegion.IsNull() ) {
      gdk_gc_set_clip_region( use_gc, m_currentClippingRegion.GetRegion() );
    }
    if( new_mask != NULL ) {
      g_object_unref( new_mask );
    }
  }
}

bool wxWindowDC::DoBlit( wxCoord xdest, wxCoord ydest, wxCoord width, wxCoord height, wxDC *source,
                         wxCoord xsrc, wxCoord ysrc, int logical_func, bool useMask,
                         wxCoord xsrcMask, wxCoord ysrcMask ) {
  wxCHECK_MSG( Ok(), false, wxT( "invalid window dc" ) );
  wxCHECK_MSG( source, false, wxT( "invalid source dc" ) );
  if( !m_window ) {
    return false;
  }
  xsrc = source->LogicalToDeviceX( xsrc );
  ysrc = source->LogicalToDeviceY( ysrc );
  wxMemoryDC *memDC = wxDynamicCast( source, wxMemoryDC );
  wxBitmap selected = source->GetSelectedBitmap();
  bool use_bitmap_method = false;
  bool is_mono = false;
  if( xsrcMask == -1 && ysrcMask == -1 ) {
    xsrcMask = xsrc;
    ysrcMask = ysrc;
  }
  if( memDC && !selected.Ok() ) {
    return false;
  }
  if( selected.Ok() ) {
    is_mono = ( selected.GetDepth() == 1 );
    if( useMask && ( selected.GetMask() ) ) {
      use_bitmap_method = true;
    } else if( is_mono ) {
      use_bitmap_method = true;
    } else if( ( xsrc == 0 ) && ( ysrc == 0 ) && ( width == selected.GetWidth() ) && ( height == selected.GetHeight() ) ) {
      use_bitmap_method = true;
    }
  }
  CalcBoundingBox( xdest, ydest );
  CalcBoundingBox( xdest + width, ydest + height );
  wxCoord xx = XLOG2DEV( xdest );
  wxCoord yy = YLOG2DEV( ydest );
  wxCoord ww = XLOG2DEVREL( width );
  wxCoord hh = YLOG2DEVREL( height );
  if( !m_currentClippingRegion.IsNull() ) {
    wxRegion tmp( xx, yy, ww, hh );
    tmp.Intersect( m_currentClippingRegion );
    if( tmp.IsEmpty() ) {
      return true;
    }
  }
  int old_logical_func = m_logicalFunction;
  SetLogicalFunction( logical_func );
  if( use_bitmap_method ) {
    wxCoord bm_width = selected.GetWidth();
    wxCoord bm_height = selected.GetHeight();
    wxCoord cx = xx;
    wxCoord cy = yy;
    wxCoord cw = ww;
    wxCoord ch = hh;
    double xsc, ysc;
    memDC->GetUserScale( &xsc, &ysc );
    bm_width = ( int )( bm_width / xsc );
    bm_height = ( int )( bm_height / ysc );
    wxCoord bm_ww = XLOG2DEVREL( bm_width );
    wxCoord bm_hh = YLOG2DEVREL( bm_height );
    wxBitmap use_bitmap = selected;
    if( ( selected.GetWidth() != bm_ww ) || ( selected.GetHeight() != bm_hh ) ) {
      wxRegion tmp( xx, yy, ww, hh );
      if( !m_currentClippingRegion.IsNull() ) {
        tmp.Intersect( m_currentClippingRegion );
      }
      tmp.GetBox( cx, cy, cw, ch );
      use_bitmap = selected.Rescale( cx - xx, cy - yy, cw, ch, bm_ww, bm_hh );
    }
    GdkBitmap *mask = ( GdkBitmap * ) NULL;
    if( useMask && use_bitmap.GetMask() ) {
      mask = use_bitmap.GetMask()->GetBitmap();
    }
    GdkGC* use_gc = is_mono ? m_textGC : m_penGC;
    GdkBitmap *new_mask = ( GdkBitmap* ) NULL;
    if( mask != NULL ) {
      if( !m_currentClippingRegion.IsNull() ) {
        GdkColor col;
        new_mask = gdk_pixmap_new( wxGetRootWindow()->window, bm_ww, bm_hh, 1 );
        GdkGC *gc = gdk_gc_new( new_mask );
        col.pixel = 0;
        gdk_gc_set_foreground( gc, &col );
        gdk_gc_set_ts_origin( gc, -xsrcMask, -ysrcMask );
        gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, bm_ww, bm_hh );
        col.pixel = 0;
        gdk_gc_set_background( gc, &col );
        col.pixel = 1;
        gdk_gc_set_foreground( gc, &col );
        gdk_gc_set_clip_region( gc, m_currentClippingRegion.GetRegion() );
        gdk_gc_set_clip_origin( gc, -cx, -cy );
        gdk_gc_set_fill( gc, GDK_OPAQUE_STIPPLED );
        gdk_gc_set_stipple( gc, mask );
        gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, bm_ww, bm_hh );
        mask = new_mask;
        g_object_unref( gc );
      }
      gdk_gc_set_clip_mask( use_gc, mask );
      if( new_mask != NULL ) {
        gdk_gc_set_clip_origin( use_gc, cx, cy );
      } else
      { gdk_gc_set_clip_origin( use_gc, cx - xsrcMask, cy - ysrcMask ); }
    }
    if( is_mono ) {
      GdkPixmap *bitmap = gdk_pixmap_new( wxGetRootWindow()->window, bm_ww, bm_hh, -1 );
      GdkGC *gc = gdk_gc_new( bitmap );
      gdk_gc_set_foreground( gc, m_textForegroundColour.GetColor() );
      gdk_gc_set_background( gc, m_textBackgroundColour.GetColor() );
      gdk_wx_draw_bitmap( bitmap, gc, use_bitmap.GetPixmap(), 0, 0, 0, 0, -1, -1 );
      gdk_draw_drawable( m_window, use_gc, bitmap, xsrc, ysrc, cx, cy, cw, ch );
      g_object_unref( bitmap );
      g_object_unref( gc );
    } else {
      gdk_draw_drawable( m_window, use_gc, use_bitmap.GetPixmap(), xsrc, ysrc, cx, cy, cw, ch );
    }
    if( mask != NULL ) {
      gdk_gc_set_clip_mask( use_gc, NULL );
      gdk_gc_set_clip_origin( use_gc, 0, 0 );
      if( !m_currentClippingRegion.IsNull() ) {
        gdk_gc_set_clip_region( use_gc, m_currentClippingRegion.GetRegion() );
      }
    }
    if( new_mask ) {
      g_object_unref( new_mask );
    }
  } else {
    if( selected.Ok() && ( ( width != ww ) || ( height != hh ) ) ) {
      wxRegion tmp( xx, yy, ww, hh );
      tmp.Intersect( m_currentClippingRegion );
      wxCoord cx, cy, cw, ch;
      tmp.GetBox( cx, cy, cw, ch );
      wxBitmap bitmap = selected.Rescale( cx - xx, cy - yy, cw, ch, ww, hh );
      gdk_draw_drawable( m_window, m_penGC, bitmap.GetPixmap(), 0, 0, cx, cy, -1, -1 );
    } else {
      GdkWindow* window = source->GetGDKWindow();
      if( !window ) {
        return false;
      }
      gdk_gc_set_subwindow( m_penGC, GDK_INCLUDE_INFERIORS );
      gdk_draw_drawable( m_window, m_penGC,
                         window,
                         xsrc, ysrc, xx, yy,
                         width, height );
      gdk_gc_set_subwindow( m_penGC, GDK_CLIP_BY_CHILDREN );
    }
  }
  SetLogicalFunction( old_logical_func );
  return true;
}

void wxWindowDC::DoDrawText( const wxString &text, wxCoord x, wxCoord y ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( !m_window ) {
    return;
  }
  if( text.empty() ) {
    return;
  }
  x = XLOG2DEV( x );
  y = YLOG2DEV( y );
  wxCHECK_RET( m_context, wxT( "no Pango context" ) );
  wxCHECK_RET( m_layout, wxT( "no Pango layout" ) );
  wxCHECK_RET( m_fontdesc, wxT( "no Pango font description" ) );
  gdk_pango_context_set_colormap( m_context, m_cmap );
  bool underlined = m_font.Ok() && m_font.GetUnderlined();
  const wxCharBuffer data = wxGTK_CONV( text );
  if( !data ) {
    return;
  }
  size_t datalen = strlen( data );
  bool needshack = underlined;
  char *hackstring = NULL;
  if( needshack ) {
    hackstring = ( char* )malloc( ( datalen + 7 ) * sizeof( char ) );
    strcpy( hackstring, "\342\200\214" );
    memcpy( &hackstring[3], data, datalen );
    strcpy( &hackstring[datalen + 3], "\342\200\214" );
    datalen += 6;
    pango_layout_set_text( m_layout, hackstring, datalen );
  } else {
    pango_layout_set_text( m_layout, data, datalen );
  }
  if( underlined ) {
    PangoAttrList *attrs = pango_attr_list_new();
    PangoAttribute *a = pango_attr_underline_new( PANGO_UNDERLINE_SINGLE );
    a->start_index = 0;
    a->end_index = datalen;
    pango_attr_list_insert( attrs, a );
    if( needshack ) {
      a = pango_attr_foreground_new( 0x0057, 0x52A9, 0xD614 );
      a->start_index = 0;
      a->end_index = 1;
      pango_attr_list_insert( attrs, a );
      a = pango_attr_foreground_new( 0x0057, 0x52A9, 0xD614 );
      a->start_index = datalen - 1;
      a->end_index = datalen;
      pango_attr_list_insert( attrs, a );
    }
    pango_layout_set_attributes( m_layout, attrs );
    pango_attr_list_unref( attrs );
  }
  int w, h;
  if( fabs( m_scaleY - 1.0 ) > 0.00001 ) {
    gint oldSize = pango_font_description_get_size( m_fontdesc );
    double size = oldSize;
    size = size * m_scaleY;
    pango_font_description_set_size( m_fontdesc, ( gint )size );
    pango_layout_set_font_description( m_layout, m_fontdesc );
    pango_layout_get_pixel_size( m_layout, &w, &h );
    if( m_backgroundMode == wxSOLID ) {
      gdk_gc_set_foreground( m_textGC, m_textBackgroundColour.GetColor() );
      gdk_draw_rectangle( m_window, m_textGC, TRUE, x, y, w, h );
      gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
    }
    if( m_owner && m_owner->GetLayoutDirection() == wxLayout_RightToLeft ) {
      gdk_draw_layout( m_window, m_textGC, x - w, y, m_layout );
    } else
    { gdk_draw_layout( m_window, m_textGC, x, y, m_layout ); }
    pango_font_description_set_size( m_fontdesc, oldSize );
    pango_layout_set_font_description( m_layout, m_fontdesc );
  } else {
    pango_layout_get_pixel_size( m_layout, &w, &h );
    if( m_backgroundMode == wxSOLID ) {
      gdk_gc_set_foreground( m_textGC, m_textBackgroundColour.GetColor() );
      gdk_draw_rectangle( m_window, m_textGC, TRUE, x, y, w, h );
      gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
    }
    if( m_owner && m_owner->GetLayoutDirection() == wxLayout_RightToLeft ) {
      gdk_draw_layout( m_window, m_textGC, x - w, y, m_layout );
    } else
    { gdk_draw_layout( m_window, m_textGC, x, y, m_layout ); }
  }
  if( underlined ) {
    pango_layout_set_attributes( m_layout, NULL );
  }
  wxCoord width = w;
  wxCoord height = h;
  width = wxCoord( width / m_scaleX );
  height = wxCoord( height / m_scaleY );
  CalcBoundingBox( x + width, y + height );
  CalcBoundingBox( x, y );
  if( hackstring ) {
    free( hackstring );
  }
}

void wxWindowDC::DoDrawRotatedText( const wxString &text, wxCoord x, wxCoord y, double angle ) {
  if( !m_window || text.empty() ) {
    return;
  }
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( wxIsNullDouble( angle ) ) {
    DrawText( text, x, y );
    return;
  }
  wxCoord w;
  wxCoord h;
  GetTextExtent( text, &w, &h, NULL, NULL, &m_font );
  wxBitmap src( w, h );
  wxMemoryDC dc;
  dc.SelectObject( src );
  dc.SetFont( GetFont() );
  dc.SetBackground( *wxBLACK_BRUSH );
  dc.SetBrush( *wxBLACK_BRUSH );
  dc.Clear();
  dc.SetTextForeground( *wxWHITE );
  dc.DrawText( text, 0, 0 );
  dc.SelectObject( wxNullBitmap );
  double rad = DegToRad( angle );
  double dx = cos( rad ),
         dy = sin( rad );
  double x2 = w * dx,
         y2 = -w * dy;
  double x4 = h * dy,
         y4 = h * dx;
  double x3 = x4 + x2,
         y3 = y4 + y2;
  wxCoord maxX = ( wxCoord )( dmax( x2, dmax( x3, x4 ) ) + 0.5 ),
          maxY = ( wxCoord )( dmax( y2, dmax( y3, y4 ) ) + 0.5 ),
          minX = ( wxCoord )( dmin( x2, dmin( x3, x4 ) ) - 0.5 ),
          minY = ( wxCoord )( dmin( y2, dmin( y3, y4 ) ) - 0.5 );
  wxImage image = src.ConvertToImage();
  image.ConvertColourToAlpha( m_textForegroundColour.Red(),
                              m_textForegroundColour.Green(),
                              m_textForegroundColour.Blue() );
  image = image.Rotate( rad, wxPoint( 0, 0 ) );
  int i_angle = ( int ) angle;
  i_angle = i_angle % 360;
  if( i_angle < 0 ) {
    i_angle += 360;
  }
  int xoffset = 0;
  if( ( i_angle >= 90.0 ) && ( i_angle < 270.0 ) ) {
    xoffset = image.GetWidth();
  }
  int yoffset = 0;
  if( ( i_angle >= 0.0 ) && ( i_angle < 180.0 ) ) {
    yoffset = image.GetHeight();
  }
  if( ( i_angle >= 0 ) && ( i_angle < 90 ) ) {
    yoffset -= ( int )( cos( rad ) * h );
  }
  if( ( i_angle >= 90 ) && ( i_angle < 180 ) ) {
    xoffset -= ( int )( sin( rad ) * h );
  }
  if( ( i_angle >= 180 ) && ( i_angle < 270 ) ) {
    yoffset -= ( int )( cos( rad ) * h );
  }
  if( ( i_angle >= 270 ) && ( i_angle < 360 ) ) {
    xoffset -= ( int )( sin( rad ) * h );
  }
  int i_x = x - xoffset;
  int i_y = y - yoffset;
  src = image;
  DoDrawBitmap( src, i_x, i_y, true );
  CalcBoundingBox( x + minX, y + minY );
  CalcBoundingBox( x + maxX, y + maxY );
}

void wxWindowDC::DoGetTextExtent( const wxString &string,
                                  wxCoord *width, wxCoord *height,
                                  wxCoord *descent, wxCoord *externalLeading,
                                  wxFont *theFont ) const {
  if( width ) {
    *width = 0;
  }
  if( height ) {
    *height = 0;
  }
  if( descent ) {
    *descent = 0;
  }
  if( externalLeading ) {
    *externalLeading = 0;
  }
  if( string.empty() ) {
    return;
  }
  if( !theFont || !theFont->Ok() ) {
    theFont = wx_const_cast( wxFont *, &m_font );
  }
  if( theFont->Ok() ) {
    pango_layout_set_font_description
    (
      m_layout,
      theFont->GetNativeFontInfo()->description
    );
  }
  const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT( string, *theFont );
  if( !dataUTF8 ) {
    return;
  }
  pango_layout_set_text( m_layout, dataUTF8, strlen( dataUTF8 ) );
  if( descent ) {
    int h;
    pango_layout_get_pixel_size( m_layout, width, &h );
    PangoLayoutIter *iter = pango_layout_get_iter( m_layout );
    int baseline = pango_layout_iter_get_baseline( iter );
    pango_layout_iter_free( iter );
    *descent = h - PANGO_PIXELS( baseline );
    if( height ) {
      *height = ( wxCoord ) h;
    }
  } else {
    pango_layout_get_pixel_size( m_layout, width, height );
  }
  if( theFont->IsOk() ) {
    pango_layout_set_font_description( m_layout, m_fontdesc );
  }
}


bool wxWindowDC::DoGetPartialTextExtents( const wxString& text,
    wxArrayInt& widths ) const {
  const size_t len = text.length();
  widths.Empty();
  widths.Add( 0, len );
  if( text.empty() ) {
    return true;
  }
  const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT( text, m_font );
  if( !dataUTF8 ) {
    wxLogLastError( wxT( "DoGetPartialTextExtents" ) );
    return false;
  }
  pango_layout_set_text( m_layout, dataUTF8, strlen( dataUTF8 ) );
  PangoLayoutIter *iter = pango_layout_get_iter( m_layout );
  PangoRectangle pos;
  pango_layout_iter_get_cluster_extents( iter, NULL, &pos );
  size_t i = 0;
  while( pango_layout_iter_next_cluster( iter ) ) {
    pango_layout_iter_get_cluster_extents( iter, NULL, &pos );
    int position = PANGO_PIXELS( pos.x );
    widths[i++] = position;
  }
  while( i < len ) {
    widths[i++] = PANGO_PIXELS( pos.x + pos.width );
  }
  pango_layout_iter_free( iter );
  return true;
}


wxCoord wxWindowDC::GetCharWidth() const {
  pango_layout_set_text( m_layout, "H", 1 );
  int w;
  pango_layout_get_pixel_size( m_layout, &w, NULL );
  return w;
}

wxCoord wxWindowDC::GetCharHeight() const {
  PangoFontMetrics *metrics = pango_context_get_metrics( m_context, m_fontdesc, pango_context_get_language( m_context ) );
  wxCHECK_MSG( metrics, -1, _T( "failed to get pango font metrics" ) );
  wxCoord h = PANGO_PIXELS( pango_font_metrics_get_descent( metrics ) +
                            pango_font_metrics_get_ascent( metrics ) );
  pango_font_metrics_unref( metrics );
  return h;
}

void wxWindowDC::Clear() {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( !m_window ) {
    return;
  }
  int width, height;
  GetSize( &width, &height );
  gdk_draw_rectangle( m_window, m_bgGC, TRUE, 0, 0, width, height );
}

void wxWindowDC::SetFont( const wxFont &font ) {
  m_font = font;
  if( m_font.Ok() ) {
    if( m_fontdesc ) {
      pango_font_description_free( m_fontdesc );
    }
    m_fontdesc = pango_font_description_copy( m_font.GetNativeFontInfo()->description );
    if( m_owner ) {
      PangoContext *oldContext = m_context;
      m_context = m_owner->GtkGetPangoDefaultContext();
      if( oldContext != m_context ) {
        if( m_layout ) {
          g_object_unref( m_layout );
        }
        m_layout = pango_layout_new( m_context );
      }
    }
    pango_layout_set_font_description( m_layout, m_fontdesc );
  }
}

void wxWindowDC::SetPen( const wxPen &pen ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( m_pen == pen ) {
    return;
  }
  m_pen = pen;
  if( !m_pen.Ok() ) {
    return;
  }
  if( !m_window ) {
    return;
  }
  gint width = m_pen.GetWidth();
  if( width <= 0 ) {
    width = 1;
  } else {
    double w = 0.5 +
               ( fabs( ( double ) XLOG2DEVREL( width ) ) +
                 fabs( ( double ) YLOG2DEVREL( width ) ) ) / 2.0;
    width = ( int )w;
    if( !width ) {
      width = 1;
    }
  }
  static const wxGTKDash dotted[] = {1, 1};
  static const wxGTKDash short_dashed[] = {2, 2};
  static const wxGTKDash wxCoord_dashed[] = {2, 4};
  static const wxGTKDash dotted_dashed[] = {3, 3, 1, 3};
  int req_nb_dash;
  const wxGTKDash *req_dash;
  GdkLineStyle lineStyle = GDK_LINE_SOLID;
  switch( m_pen.GetStyle() ) {
    case wxUSER_DASH: {
      lineStyle = GDK_LINE_ON_OFF_DASH;
      req_nb_dash = m_pen.GetDashCount();
      req_dash = ( wxGTKDash* )m_pen.GetDash();
      break;
    }
    case wxDOT: {
      lineStyle = GDK_LINE_ON_OFF_DASH;
      req_nb_dash = 2;
      req_dash = dotted;
      break;
    }
    case wxLONG_DASH: {
      lineStyle = GDK_LINE_ON_OFF_DASH;
      req_nb_dash = 2;
      req_dash = wxCoord_dashed;
      break;
    }
    case wxSHORT_DASH: {
      lineStyle = GDK_LINE_ON_OFF_DASH;
      req_nb_dash = 2;
      req_dash = short_dashed;
      break;
    }
    case wxDOT_DASH: {
      lineStyle = GDK_LINE_ON_OFF_DASH;
      req_nb_dash = 4;
      req_dash = dotted_dashed;
      break;
    }
    case wxTRANSPARENT:
    case wxSTIPPLE_MASK_OPAQUE:
    case wxSTIPPLE:
    case wxSOLID:
    default: {
      lineStyle = GDK_LINE_SOLID;
      req_dash = ( wxGTKDash* )NULL;
      req_nb_dash = 0;
      break;
    }
  }
  if( req_dash && req_nb_dash ) {
    wxGTKDash *real_req_dash = new wxGTKDash[req_nb_dash];
    if( real_req_dash ) {
      for( int i = 0; i < req_nb_dash; i++ ) {
        real_req_dash[i] = req_dash[i] * width;
      }
      gdk_gc_set_dashes( m_penGC, 0, real_req_dash, req_nb_dash );
      delete[] real_req_dash;
    } else {
      gdk_gc_set_dashes( m_penGC, 0, ( wxGTKDash* )req_dash, req_nb_dash );
    }
  }
  GdkCapStyle capStyle = GDK_CAP_ROUND;
  switch( m_pen.GetCap() ) {
    case wxCAP_PROJECTING:
    { capStyle = GDK_CAP_PROJECTING; break; }
    case wxCAP_BUTT:
    { capStyle = GDK_CAP_BUTT;       break; }
    case wxCAP_ROUND:
    default: {
      if( width <= 1 ) {
        width = 0;
        capStyle = GDK_CAP_NOT_LAST;
      } else {
        capStyle = GDK_CAP_ROUND;
      }
      break;
    }
  }
  GdkJoinStyle joinStyle = GDK_JOIN_ROUND;
  switch( m_pen.GetJoin() ) {
    case wxJOIN_BEVEL:
    { joinStyle = GDK_JOIN_BEVEL; break; }
    case wxJOIN_MITER:
    { joinStyle = GDK_JOIN_MITER; break; }
    case wxJOIN_ROUND:
    default:
    { joinStyle = GDK_JOIN_ROUND; break; }
  }
  gdk_gc_set_line_attributes( m_penGC, width, lineStyle, capStyle, joinStyle );
  m_pen.GetColour().CalcPixel( m_cmap );
  gdk_gc_set_foreground( m_penGC, m_pen.GetColour().GetColor() );
}

void wxWindowDC::SetBrush( const wxBrush &brush ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( m_brush == brush ) {
    return;
  }
  m_brush = brush;
  if( !m_brush.Ok() ) {
    return;
  }
  if( !m_window ) {
    return;
  }
  m_brush.GetColour().CalcPixel( m_cmap );
  gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() );
  gdk_gc_set_fill( m_brushGC, GDK_SOLID );
  if( ( m_brush.GetStyle() == wxSTIPPLE ) && ( m_brush.GetStipple()->Ok() ) ) {
    if( m_brush.GetStipple()->GetDepth() != 1 ) {
      gdk_gc_set_fill( m_brushGC, GDK_TILED );
      gdk_gc_set_tile( m_brushGC, m_brush.GetStipple()->GetPixmap() );
    } else {
      gdk_gc_set_fill( m_brushGC, GDK_STIPPLED );
      gdk_gc_set_stipple( m_brushGC, m_brush.GetStipple()->GetPixmap() );
    }
  }
  if( ( m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE ) && ( m_brush.GetStipple()->GetMask() ) ) {
    gdk_gc_set_fill( m_textGC, GDK_OPAQUE_STIPPLED );
    gdk_gc_set_stipple( m_textGC, m_brush.GetStipple()->GetMask()->GetBitmap() );
  }
  if( m_brush.IsHatch() ) {
    gdk_gc_set_fill( m_brushGC, GDK_STIPPLED );
    int num = m_brush.GetStyle() - wxBDIAGONAL_HATCH;
    gdk_gc_set_stipple( m_brushGC, hatches[num] );
  }
}

void wxWindowDC::SetBackground( const wxBrush &brush ) {
  /* CMB 21/7/98: Added SetBackground. Sets background brush
     for Clear() and bg colour for shapes filled with cross-hatch brush */
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( m_backgroundBrush == brush ) {
    return;
  }
  m_backgroundBrush = brush;
  if( !m_backgroundBrush.Ok() ) {
    return;
  }
  if( !m_window ) {
    return;
  }
  m_backgroundBrush.GetColour().CalcPixel( m_cmap );
  gdk_gc_set_background( m_brushGC, m_backgroundBrush.GetColour().GetColor() );
  gdk_gc_set_background( m_penGC, m_backgroundBrush.GetColour().GetColor() );
  gdk_gc_set_background( m_bgGC, m_backgroundBrush.GetColour().GetColor() );
  gdk_gc_set_foreground( m_bgGC, m_backgroundBrush.GetColour().GetColor() );
  gdk_gc_set_fill( m_bgGC, GDK_SOLID );
  if( ( m_backgroundBrush.GetStyle() == wxSTIPPLE ) && ( m_backgroundBrush.GetStipple()->Ok() ) ) {
    if( m_backgroundBrush.GetStipple()->GetDepth() != 1 ) {
      gdk_gc_set_fill( m_bgGC, GDK_TILED );
      gdk_gc_set_tile( m_bgGC, m_backgroundBrush.GetStipple()->GetPixmap() );
    } else {
      gdk_gc_set_fill( m_bgGC, GDK_STIPPLED );
      gdk_gc_set_stipple( m_bgGC, m_backgroundBrush.GetStipple()->GetPixmap() );
    }
  }
  if( m_backgroundBrush.IsHatch() ) {
    gdk_gc_set_fill( m_bgGC, GDK_STIPPLED );
    int num = m_backgroundBrush.GetStyle() - wxBDIAGONAL_HATCH;
    gdk_gc_set_stipple( m_bgGC, hatches[num] );
  }
}

void wxWindowDC::SetLogicalFunction( int function ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( m_logicalFunction == function ) {
    return;
  }
  if( !m_window ) {
    return;
  }
  GdkFunction mode;
  switch( function ) {
    case wxXOR:
      mode = GDK_XOR;
      break;
    case wxINVERT:
      mode = GDK_INVERT;
      break;
    case wxOR_REVERSE:
      mode = GDK_OR_REVERSE;
      break;
    case wxAND_REVERSE:
      mode = GDK_AND_REVERSE;
      break;
    case wxCLEAR:
      mode = GDK_CLEAR;
      break;
    case wxSET:
      mode = GDK_SET;
      break;
    case wxOR_INVERT:
      mode = GDK_OR_INVERT;
      break;
    case wxAND:
      mode = GDK_AND;
      break;
    case wxOR:
      mode = GDK_OR;
      break;
    case wxEQUIV:
      mode = GDK_EQUIV;
      break;
    case wxNAND:
      mode = GDK_NAND;
      break;
    case wxAND_INVERT:
      mode = GDK_AND_INVERT;
      break;
    case wxCOPY:
      mode = GDK_COPY;
      break;
    case wxNO_OP:
      mode = GDK_NOOP;
      break;
    case wxSRC_INVERT:
      mode = GDK_COPY_INVERT;
      break;
    case wxNOR:
      mode = GDK_COPY;
      break;
    default:
      wxFAIL_MSG( wxT( "unsupported logical function" ) );
      mode = GDK_COPY;
  }
  m_logicalFunction = function;
  gdk_gc_set_function( m_penGC, mode );
  gdk_gc_set_function( m_brushGC, mode );
  gdk_gc_set_function( m_textGC, mode );
}

void wxWindowDC::SetTextForeground( const wxColour &col ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( !col.Ok() || ( m_textForegroundColour == col ) ) {
    return;
  }
  m_textForegroundColour = col;
  if( m_window ) {
    m_textForegroundColour.CalcPixel( m_cmap );
    gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
  }
}

void wxWindowDC::SetTextBackground( const wxColour &col ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( !col.Ok() || ( m_textBackgroundColour == col ) ) {
    return;
  }
  m_textBackgroundColour = col;
  if( m_window ) {
    m_textBackgroundColour.CalcPixel( m_cmap );
    gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() );
  }
}

void wxWindowDC::SetBackgroundMode( int mode ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  m_backgroundMode = mode;
}

void wxWindowDC::SetPalette( const wxPalette & ( palette ) ) {
  wxFAIL_MSG( wxT( "wxWindowDC::SetPalette not implemented" ) );
}

void wxWindowDC::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( !m_window ) {
    return;
  }
  wxRect rect;
  rect.x = XLOG2DEV( x );
  rect.y = YLOG2DEV( y );
  rect.width = XLOG2DEVREL( width );
  rect.height = YLOG2DEVREL( height );
  if( m_owner && m_owner->m_wxwindow && ( m_owner->GetLayoutDirection() == wxLayout_RightToLeft ) ) {
    rect.x -= rect.width;
  }
  if( !m_currentClippingRegion.IsNull() ) {
    m_currentClippingRegion.Intersect( rect );
  } else
  { m_currentClippingRegion.Union( rect ); }
  #if USE_PAINT_REGION
  if( !m_paintClippingRegion.IsNull() ) {
    m_currentClippingRegion.Intersect( m_paintClippingRegion );
  }
  #endif
  wxCoord xx, yy, ww, hh;
  m_currentClippingRegion.GetBox( xx, yy, ww, hh );
  wxDC::DoSetClippingRegion( xx, yy, ww, hh );
  gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
  gdk_gc_set_clip_region( m_brushGC, m_currentClippingRegion.GetRegion() );
  gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
  gdk_gc_set_clip_region( m_bgGC, m_currentClippingRegion.GetRegion() );
}

void wxWindowDC::DoSetClippingRegionAsRegion( const wxRegion &region ) {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  if( region.Empty() ) {
    DestroyClippingRegion();
    return;
  }
  if( !m_window ) {
    return;
  }
  if( !m_currentClippingRegion.IsNull() ) {
    m_currentClippingRegion.Intersect( region );
  } else
  { m_currentClippingRegion.Union( region ); }
  #if USE_PAINT_REGION
  if( !m_paintClippingRegion.IsNull() ) {
    m_currentClippingRegion.Intersect( m_paintClippingRegion );
  }
  #endif
  wxCoord xx, yy, ww, hh;
  m_currentClippingRegion.GetBox( xx, yy, ww, hh );
  wxDC::DoSetClippingRegion( xx, yy, ww, hh );
  gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
  gdk_gc_set_clip_region( m_brushGC, m_currentClippingRegion.GetRegion() );
  gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
  gdk_gc_set_clip_region( m_bgGC, m_currentClippingRegion.GetRegion() );
}

void wxWindowDC::DestroyClippingRegion() {
  wxCHECK_RET( Ok(), wxT( "invalid window dc" ) );
  wxDC::DestroyClippingRegion();
  m_currentClippingRegion.Clear();
  #if USE_PAINT_REGION
  if( !m_paintClippingRegion.IsEmpty() ) {
    m_currentClippingRegion.Union( m_paintClippingRegion );
  }
  #endif
  if( !m_window ) {
    return;
  }
  if( m_currentClippingRegion.IsEmpty() ) {
    gdk_gc_set_clip_rectangle( m_penGC, ( GdkRectangle * ) NULL );
    gdk_gc_set_clip_rectangle( m_brushGC, ( GdkRectangle * ) NULL );
    gdk_gc_set_clip_rectangle( m_textGC, ( GdkRectangle * ) NULL );
    gdk_gc_set_clip_rectangle( m_bgGC, ( GdkRectangle * ) NULL );
  } else {
    gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
    gdk_gc_set_clip_region( m_brushGC, m_currentClippingRegion.GetRegion() );
    gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
    gdk_gc_set_clip_region( m_bgGC, m_currentClippingRegion.GetRegion() );
  }
}

void wxWindowDC::Destroy() {
  if( m_penGC ) {
    wxFreePoolGC( m_penGC );
  }
  m_penGC = ( GdkGC* ) NULL;
  if( m_brushGC ) {
    wxFreePoolGC( m_brushGC );
  }
  m_brushGC = ( GdkGC* ) NULL;
  if( m_textGC ) {
    wxFreePoolGC( m_textGC );
  }
  m_textGC = ( GdkGC* ) NULL;
  if( m_bgGC ) {
    wxFreePoolGC( m_bgGC );
  }
  m_bgGC = ( GdkGC* ) NULL;
}

void wxWindowDC::SetDeviceOrigin( wxCoord x, wxCoord y ) {
  m_deviceOriginX = x;
  m_deviceOriginY = y;
  ComputeScaleAndOrigin();
}

void wxWindowDC::SetAxisOrientation( bool xLeftRight, bool yBottomUp ) {
  m_signX = ( xLeftRight ?  1 : -1 );
  m_signY = ( yBottomUp  ? -1 :  1 );
  if( m_owner && m_owner->m_wxwindow && ( m_owner->GetLayoutDirection() == wxLayout_RightToLeft ) ) {
    m_signX = -m_signX;
  }
  ComputeScaleAndOrigin();
}

void wxWindowDC::ComputeScaleAndOrigin() {
  const wxRealPoint origScale( m_scaleX, m_scaleY );
  wxDC::ComputeScaleAndOrigin();
  if( wxRealPoint( m_scaleX, m_scaleY ) != origScale && m_pen.Ok() ) {
    wxPen pen = m_pen;
    m_pen = wxNullPen;
    SetPen( pen );
  }
}

wxSize wxWindowDC::GetPPI() const {
  return wxSize( ( int )( m_mm_to_pix_x * 25.4 + 0.5 ), ( int )( m_mm_to_pix_y * 25.4 + 0.5 ) );
}

int wxWindowDC::GetDepth() const {
  return gdk_drawable_get_depth( m_window );
}

IMPLEMENT_DYNAMIC_CLASS( wxPaintDC, wxClientDC )

static void wxLimitRegionToSize( wxRegion& region, const wxSize& sz ) {
  wxRect originalRect = region.GetBox();
  wxRect rect( originalRect );
  if( rect.width + rect.x > sz.x ) {
    rect.width = sz.x - rect.x;
  }
  if( rect.height + rect.y > sz.y ) {
    rect.height = sz.y - rect.y;
  }
  if( rect != originalRect ) {
    region = wxRegion( rect );
    wxLogTrace( wxT( "painting" ), wxT( "Limiting region from %d, %d, %d, %d to %d, %d, %d, %d\n" ),
                originalRect.x, originalRect.y, originalRect.width, originalRect.height,
                rect.x, rect.y, rect.width, rect.height );
  }
}

wxPaintDC::wxPaintDC( wxWindow *win )
  : wxClientDC( win ) {
  #if USE_PAINT_REGION
  if( !win )
  { return; }
  if( !win->m_clipPaintRegion ) {
    return;
  }
  wxSize sz = win->GetSize();
  m_paintClippingRegion = win->m_nativeUpdateRegion;
  wxLimitRegionToSize( m_paintClippingRegion, sz );
  GdkRegion *region = m_paintClippingRegion.GetRegion();
  if( region ) {
    m_currentClippingRegion.Union( m_paintClippingRegion );
    wxLimitRegionToSize( m_currentClippingRegion, sz );
    if( sz.x <= 0 || sz.y <= 0 ) {
      return ;
    }
    gdk_gc_set_clip_region( m_penGC, region );
    gdk_gc_set_clip_region( m_brushGC, region );
    gdk_gc_set_clip_region( m_textGC, region );
    gdk_gc_set_clip_region( m_bgGC, region );
  }
  #endif
}

IMPLEMENT_DYNAMIC_CLASS( wxClientDC, wxWindowDC )

wxClientDC::wxClientDC( wxWindow *win )
  : wxWindowDC( win ) {
  if( !win )
  { return; }
  #ifdef __WXUNIVERSAL__
  wxPoint ptOrigin = win->GetClientAreaOrigin();
  SetDeviceOrigin( ptOrigin.x, ptOrigin.y );
  wxSize size = win->GetClientSize();
  SetClippingRegion( wxPoint( 0, 0 ), size );
  #endif
}

void wxClientDC::DoGetSize( int *width, int *height ) const {
  wxCHECK_RET( m_owner, _T( "GetSize() doesn't work without window" ) );
  m_owner->GetClientSize( width, height );
}

class wxDCModule : public wxModule {
  public:
    bool OnInit();
    void OnExit();

  private:
    DECLARE_DYNAMIC_CLASS( wxDCModule )
};

IMPLEMENT_DYNAMIC_CLASS( wxDCModule, wxModule )

bool wxDCModule::OnInit() {
  wxInitGCPool();
  return true;
}

void wxDCModule::OnExit() {
  wxCleanUpGCPool();
}
